package com.loongtech.core.jpa.manager;

import jodd.util.StringUtil;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.CollectionUtils;
import org.hibernate.*;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.metadata.ClassMetadata;

import javax.persistence.EntityManager;
import javax.persistence.FetchType;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.util.*;
import java.util.Map.Entry;

@Transactional
public abstract class ManagerBase<T> implements Serializable {
	private static final long serialVersionUID = 1L;
	private static final List<LockModeType> LOCK_MODE_TYPE_LIST = Arrays.asList(LockModeType.values());
	private Class<T> entityClass = getSuperClassGenricType(getClass());
	protected boolean QUERY_CACHE_DEFAULT = false;		//是否默认启动查询缓存
	@PersistenceContext
	EntityManager em;
	
	/**
	 * 获取正在管理的Entity类型
	 * @return
	 */
	protected Class<T> getEntityClass() {
		if(entityClass.equals(Object.class)) {
			throw new RuntimeException("无法管理Object类型");
		}
		return entityClass;
	}
	
	public void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
	/**
	 * 工具方法处理get/find/count的相同逻辑
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
	 * @param params 过滤/排序/加载规则/查询缓存条件
	 * @return
	 */
	protected Criteria baseUitl(int firstResult, int maxResults, Object...params) {
		List<Object> settings = Arrays.asList(params);
		
		/* 提出分组参数 */
		boolean paramsQueryCache = QUERY_CACHE_DEFAULT;							//启动查询缓存
//		LockMode paramsLock = null;													//数据库锁
		Map<Object, Object> paramsFilter = new LinkedHashMap<Object, Object>();		//过滤条件
		Map<Object, Object> paramsOrder = new LinkedHashMap<Object, Object>();		//排序规则
		Map<Object, Object> paramsFetch = new LinkedHashMap<Object, Object>();		//延迟加载规则
		
		//参数 启动查询缓存
        if(settings.contains(QueryCache.TRUE)) {
            paramsQueryCache = true;
            settings.remove(QueryCache.TRUE);
        } else if(settings.contains(QueryCache.FALSE)) {
            paramsQueryCache = false;
            settings.remove(QueryCache.FALSE);
        }
		
		//锁信息
//		if(CollectionUtils.containsAny(settings, LOCK_MODE_TYPE_LIST)) {
//			for (Iterator<Object> iter = settings.iterator(); iter.hasNext();) {
//				Object pm = iter.next();
//				if (pm instanceof LockModeType) {
//					paramsLock = (LockMode.valueOf(((LockModeType) pm).name()));
//					iter.remove();
//				}
//			}
//		}
		
		//处理成对参数
		int len = settings.size();
		for (int i = 0; i < len; i += 2) {
			Object key = settings.get(i);
			Object val = settings.get(i + 1);
			
			//参数 排序规则
			if(val instanceof OrderBy) {
				paramsOrder.put(key, val);
			} else if(val instanceof FetchType) {		//参数 延迟加载规则
				paramsFetch.put(key, val == FetchType.EAGER ? FetchMode.JOIN : FetchMode.SELECT);
			} else {									//参数 过滤条件
				paramsFilter.put(key, val);
			}
		}
		
		/* 主体 */
		Criteria crit = getSession().createCriteria(getEntityClass());
		crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		
		//分页
		if (firstResult != 0 || maxResults != Integer.MAX_VALUE) {
			crit.setFirstResult(firstResult);
			crit.setMaxResults(maxResults);
		}
		
		//启动查询缓存
		if(paramsQueryCache) {
			crit.setCacheable(true);
		}
		
		//设置过滤条件
		for(Entry<Object, Object> entry : paramsFilter.entrySet()) {
			String k = entry.getKey().toString();
			Object v = entry.getValue();
			
			if (v == null) {
				crit.add(Restrictions.isNull(k));
			} else if(v instanceof Collection<?>) {
				Collection<?> coll = (Collection<?>)v;
				if(coll.isEmpty()) {
					crit.add(Restrictions.isNull(k));
				} else {
					crit.add(Restrictions.in(k, coll));
				}
			} else if(v instanceof Object[]) {
				Object[] oary = (Object[]) v;
				if(oary.length == 0) {
					crit.add(Restrictions.isNull(k));
				} else {
					crit.add(Restrictions.in(k, oary));
				}
			} else {
				crit.add(Restrictions.eq(k, v));
			}
		}
		
		//设置排序规则
		for(Entry<Object, Object> entry : paramsOrder.entrySet()) {
			String k = entry.getKey().toString();
			OrderBy v = (OrderBy) entry.getValue();
			
			crit.addOrder(v == OrderBy.ASC ? Order.asc(k) : Order.desc(k));
		}
		
		//设置延迟加载规则
		for(Entry<Object, Object> entry : paramsFetch.entrySet()) {
			String k = entry.getKey().toString();
			FetchMode v = (FetchMode) entry.getValue();
			
			crit.setFetchMode(k, v);
		}
		
		//设置数据库锁
//		if(paramsLock != null) {
//			crit.setLockMode(paramsLock);
//		}
		
		return crit;
	}
	
	/**
	 * 工具方法，根据查询字符串语句获取条件的对象
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
	 * @param queryStr 查询字符串
	 * @param params 占位符的实际值
	 * @return
	 */
	protected Query queryBaseUtil(int firstResult, int maxResults, String queryStr, Object...params) {
		List<Object> settings = Arrays.asList(params);
		
		Query query = getSession().createQuery(queryStr);
		
		//设置分页信息
		if (firstResult != 0 || maxResults != Integer.MAX_VALUE) {
			query.setFirstResult(firstResult);
			query.setMaxResults(maxResults);
		}
		
		//缓存信息
		if(settings.contains(QueryCache.TRUE)) {
			query.setCacheable(true);
			settings.remove(QueryCache.TRUE);
		} else if(settings.contains(QueryCache.FALSE)) {
			query.setCacheable(false);
			settings.remove(QueryCache.FALSE);
		} else {
			query.setCacheable(QUERY_CACHE_DEFAULT);
		}
		
		//锁信息
		if(CollectionUtils.containsAny(settings, LOCK_MODE_TYPE_LIST)) {
			for (Iterator<Object> iter = settings.iterator(); iter.hasNext();) {
				Object pm = iter.next();
				if (pm instanceof LockModeType) {
					query.setLockOptions(new LockOptions(LockMode.valueOf(((LockModeType) pm).name())));
					iter.remove();
				}
			}
		}
		
		//无参数时 现在就可以返回结果了
		if(settings.size() == 0) return query;
		
		//有(:)冒号的模式
		if(queryStr.contains(":")) {
			//分别取出键值
			int length = settings.size();
			Object[] keys = new Object[length / 2];
			Object[] values = new Object[length / 2];
			for(int i = 0; i < length; i++) {
				if(i % 2 == 0) keys[i / 2] = settings.get(i);
				else values[i / 2] = settings.get(i);
			}
			
			for(int i = 0; i < keys.length; i++) {
				String k = keys[i].toString();
				Object v = values[i];
				
				if (v instanceof Collection<?>) {
					Collection<?> coll = (Collection<?>)v;
					if(coll.isEmpty()) {
						query.setParameter(k, null);
					} else {
						query.setParameterList(k, coll);
					}
				} else if (v instanceof Object[]) {
					Object[] oary = (Object[]) v;
					if(oary.length == 0) {
						query.setParameter(k, null);
					} else {
						query.setParameterList(k, oary);
					}
				} else {
					query.setParameter(k, v);
				}
			}
		} else {
			int length = settings.size();
			for (int i = 0; i < length; i++) {
				query.setParameter(i, settings.get(i));
			}
		}
		
		return query;
	}
	
	/**
	 * 工具方法，获取符合条件的对象数量
	 * @param params 过滤/加载规则/查询缓存条件
	 * @return 符合条件的对象集合
	 */
	protected int countUtil(Object... params) {
		Criteria crit = baseUitl(0, Integer.MAX_VALUE, params);
		
		Long result = (Long) crit.setProjection(Projections.rowCount()).uniqueResult();
        return result.intValue();
	}
/**
 * Author: yuri
 * Description: 使用query的count方法
 * Date: 15:28 2017/5/22
 */
    protected int countNum( String queryString, Object... params) {
        queryString = "select count(1)" + queryString;
        Query query = queryBaseUtil(0, Integer.MAX_VALUE, queryString, params);
        Long result = (Long) query.uniqueResult();
        return result.intValue();
    }
	
	/**
	 * 工具方法，获取符合条件的对象
	 * @param params 过滤/排序/加载规则/查询缓存条件
	 * @return 符合条件的对象集合
	 */
	@SuppressWarnings("unchecked")
	protected T getUtil(Object... params) {
		Criteria crit = baseUitl(0, Integer.MAX_VALUE, params);
		
		List<T> result = crit.list();
		
		if(result.isEmpty()) return null;
		else return result.get(0);
	}
	
	/**
	 * 工具方法，获取符合条件的对象集合
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
	 * @param params 过滤/排序/加载规则/查询缓存条件
	 * @return 符合条件的对象集合
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findUtil(int firstResult, int maxResults, Object...params) {
		Criteria crit = baseUitl(firstResult, maxResults, params);
		
		return crit.list();
	}
	
	/**
	 * 工具方法，根据查询字符串语句获取条件的对象
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
	 * @param queryStr 查询字符串
	 * @param params 占位符的实际值
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <O> List<O> queryUtil(int firstResult, int maxResults, String queryStr, Object...params) {
		return queryBaseUtil(firstResult, maxResults, queryStr, params).list();
	}
	
	/**
	 * 获得主键的名称
	 * @return 对象的主键属性名称
	 */
	protected String getIdName() {
		ClassMetadata meta = getSession().getSessionFactory().getClassMetadata(getEntityClass());
		String idName = meta.getIdentifierPropertyName();
		return idName;
	}
	
	/**
	 * 获取EntityManager
	 * @return
	 */
	public EntityManager getEntityManager() {
		return em;
	}
	
	/**
	 * 获取Session
	 * @return
	 */
	public Session getSession() {
		return (Session) em.getDelegate();
	}
	
	/**
	 * 获取无状态Session
	 * @return
	 */
	public StatelessSession getStatelessSession() {
		return getSession().getSessionFactory().openStatelessSession();
	}
	
	/**
	 * 获取JDBC Connection连接
	 * @return
	 */
	public Connection getConnetion() {
		SessionImplementor sim = (SessionImplementor) getSession();
		return sim.connection();
	}
	
	/**
	 * 获得对象主键的值
	 * @param entity 处理的对象
	 * @return 主键的值
	 */
	public Serializable getId(T entity) {
		Serializable result;
		try {
			result = (Serializable) PropertyUtils.getProperty(entity, getIdName());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		return result;
	}
	
	/**
	 * 获取当前对象锁模式
	 */
	public LockModeType getLockMode(Object entity) {
		return em.getLockMode(entity);
	}
	
	/**
	 * 清空缓冲区，提交所有修改
	 */
	public void flush() {
		em.flush();
	}
	
	/**
	 * 清空缓存
	 */
	public void clear() {
		em.clear();
	}
	
	/**
	 * 清除对象的一级缓存
	 * @param entity 被操作的对象
	 */
	public void evict(T entity) {
		getSession().evict(entity);
	}
	
	/**
	 * 清除对象的一级缓存
	 * @param entitys 被操作的对象集合
	 */
	public void evict(List<T> entitys) {
		for(T entity : entitys) {
			this.evict(entity);
		}
	}
	
	/**
	 * 重新载入对象
	 * @param entity
	 */
	public void refresh(Object entity) {
		em.refresh(entity);
	}
	
	/**
	 * 重新载入对象
	 * @param entity
	 */
	public void refresh(Object entity, LockModeType lockModeType) {
		em.refresh(entity, lockModeType);
	}
	
	/**
     * 对象的属性是否唯一
     * @param entity 检查的对象
     * @param uniquePropNames 对象的属性集合
     * @return 如果唯一返回true，否则返回false
     */
    public boolean isUnique(T entity, String... uniquePropNames) {
    	Criteria crit = getSession().createCriteria(getEntityClass());
    	crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		crit.setProjection(Projections.rowCount());
		try {
			for (String propName : uniquePropNames) {
				crit.add(Restrictions.eq(propName, PropertyUtils.getProperty(entity, propName)));
			}

			String idName = getIdName();
			Serializable id = getId(entity);
			if (id != null)
				crit.add(Restrictions.not(Restrictions.eq(idName, id)));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return (Integer) crit.uniqueResult() == 0;
    }
    
    /**
	 * 保存或更新对象
	 * @param entity 被操作的对象
	 */
	public T merge(T entity) {
		return em.merge(entity);
	}
	
	/**
	 * 保存或更新对象
	 * @param entitys 被操作的对象集合
	 */
	public void merge(List<T> entitys) {
		for(T entity : entitys) {
			em.merge(entity);
		}
	}
	
	/**
	 * 保存对象
	 * @param entity 被操作的对象
	 * @return 数据库对象的主键
	 */
	public Serializable persist(T entity) {
    	return getSession().save(entity);
    }
	
	/**
	 * 删除对象
	 * @param entity 被操作的对象
	 */
    public void remove(T entity) {
    	em.remove(entity);
    }
    
    /**
     * 删除多条记录
     * @param list 被操作的对象List
     */
    public void remove(List<T> list) {
    	for (T t : list) {
    		em.remove(t);
		}
    }
    
    /**
     * 通过主键ID删除对象
     * @param id 被操作对象的主键ID
     */
    public void removeById(Serializable id) {
    	remove(getReference(id));
    }
	
	/**
	 * 获取对象的总数
	 */
	public int countAll(Object...params) {
		return countUtil(params);
	}
	
	/**
     * 获取符合条件的对象总数
     * @param params 过滤条件
     * @return 符合条件的对象总数
     */
    public int countBy(Object...params) {
    	return countUtil(params);
    }
    
    /**
	 * 获得某个对象的控制权(加悲观锁)
	 * @param id 对象的主键
	 * @return 符合条件的对象
	 */
	public T hold(Serializable id) {
		T entity = getReference(id);
		
		//当前对象锁状态
		LockModeType clk = getLockMode(entity);
		
		//未实例化
		if(clk == LockModeType.NONE) {
			entity = get(id, LockModeType.READ);
		} else	{ //已实例化
			//之前未加悲观锁
			if(clk != LockModeType.READ) {
				//如果原本对象已经修改过 那么此处还是会抛乐观锁异常
				flush();
				refresh(entity, LockModeType.READ);
			}
		}

    	return entity;
    }
	
	/**
     * 通过主键获取代理对象
     * @param id 对象的主键
     * @return 符合条件的代理对象
     */
	public T getReference(Serializable id) {
		return em.getReference(getEntityClass(), id);
    }
	
	/**
     * 通过主键获取对象
     * @param id 对象的主键
     * @return 符合条件的对象
     */
	public T get(Serializable id) {
		return em.find(getEntityClass(), id);
    }
	
	/**
     * 通过主键获取对象
     * @param id 对象的主键
     * @return 符合条件的对象
     */
	public T get(Serializable id, LockModeType lockModeType) {
		return em.find(getEntityClass(), id, lockModeType);
    }
    
	/**
	 * 通过属性获取对象
	 * @param params 过滤条件
	 * @return 符合条件的对象
	 */
    public T getBy(Object...params) {
		return getUtil(params);
	}
    
    /**
	 * 根据查询字符串获取数据(取第一个结果)
	 * @param queryStr 查询字符串
	 * @param params 占位符的实际值
	 * @return
	 */
	public <O> O getByQuery(String queryStr, Object...params) {
		List<O> list = findByQuery(0, 1, queryStr, params);
		
		if(list == null || list.isEmpty()) {return null;}
        else return list.get(0);
	}
	
	/**
	 * 获取所有对象集合
	 * @return 所有对象集合
	 */
	public List<T> findAll() {
		return findUtil(0, Integer.MAX_VALUE);
    }
	
	/**
	 * 获取所有对象集合，支持分页
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
	 * @return 符合条件的数据集合
	 */
	public List<T> findAll(int firstResult, int maxResults) {
		return findUtil(firstResult, maxResults);
	}
	
	/**
	 * 获取所有对象集合，支持排序
	 * @param ascOrDesc 排序的属性参数
	 * @return 符合条件的数据集合
	 */
	public List<T> findAll(Object...ascOrDesc) {
		return findUtil(0, Integer.MAX_VALUE, ascOrDesc);
	}
	
	/**
	 * 获取所有对象集合，支持排序，支持分页
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
	 * @param ascOrDesc 排序的属性参数
	 * @return 符合条件的数据集合
	 */
	public List<T> findAll(int firstResult, int maxResults, Object...ascOrDesc) {
		return findUtil(firstResult, maxResults, ascOrDesc);
	}
	
	/**
     * 获取符合条件的对象集合
     * @param params 过滤及排序条件
     * @return 符合条件的对象集合
     */
	public List<T> findBy(Object...params) {
		return findUtil(0, Integer.MAX_VALUE, params);
	}
	
	/**
	 * 获取符合条件的对象集合，支持排序，支持分页
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
     * @param params 过滤及排序条件
     * @return 符合条件的对象集合
	 */
	public List<T> findBy(int firstResult, int maxResults, Object...params) {
		return findUtil(firstResult, maxResults, params);
	}
	
	/**
	 * 根据查询字符串获取数据
	 * @param queryStr queryStr语句
	 * @param params 占位符的实际值
	 * @return
	 */
	public <O> List<O> findByQuery(String queryStr, Object...params) {
		return queryUtil(0, Integer.MAX_VALUE, queryStr, params);
	}
	
	/**
	 * 根据查询字符串获取数据，支持分页
	 * @param firstResult 首条数据的位置
	 * @param maxResults 返回数据的最大数
	 * @param queryStr 查询字符串
	 * @param params 占位符的实际值
	 * @return
	 */
	public <O> List<O> findByQuery(int firstResult, int maxResults, String queryStr, Object...params) {
		return queryUtil(firstResult, maxResults, queryStr, params);
	}
	
	/**
     * 执行给定的查询语句
     * @param queryStr
     * @param params
     * @return
     */
    public int executeUpdate(String queryStr, Object...params) {
		return queryBaseUtil(0, Integer.MAX_VALUE, queryStr, params).executeUpdate();
    }
    
    /**
	 * 获得类泛型第一个参数的Class
	 * @param clazz
	 * @return
	 */
	private static <T> Class<T> getSuperClassGenricType(Class<?> clazz) {
		return getSuperClassGenricType(clazz, 0);
	}
	
	/**
	 * createNativeQuery
	 * @param sql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <O> List<O> queryBySql(String sql) {
		if(StringUtil.isEmpty(sql)){
			return null;
		}
		return  (List<O>)getSession().createSQLQuery(sql).list() ;
	}

    /**
     * excuteNative
     * @param sql
     * @return
     */
    public Integer excuteBySql(String sql) {
        if(StringUtil.isEmpty(sql)){
            return null;
        }
        SQLQuery query = getSession().createSQLQuery(sql);
        return query.executeUpdate();
    }

    /**
     * createHqlUpdate
     * @param hql
     * @return
     */
    public void excuteUpdateByHql(String hql, Object... objs) {
        if(StringUtil.isEmpty(hql)){
            return;
        }
        Query query = getSession().createQuery(hql);
        for (int i = 0; i < objs.length; i++) {
            query.setParameter(i, objs[i]);
        }
        query.executeUpdate();
    }

    /**
     * createHqlUpdate
     * @param hql
     * @return
     */
    public Long countByHql(String hql, Object... objs) {
        if(StringUtil.isEmpty(hql)){
            return -1L;
        }
            Query query = getSession().createQuery(hql);
        for (int i = 0; i < objs.length; i++) {
            query.setParameter(i, objs[i]);
        }
        return (Long) query.uniqueResult();
    }

	/**
	 * 获得类泛型参数的Class
	 * @param clazz
	 * @param index	第几个参数, 从0开始
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static <T> Class<T> getSuperClassGenricType(Class<?> clazz, int index) {
		Type genType = clazz.getGenericSuperclass();
		// 如果不是ParameterizedType接口的子类 就不可能含有泛型参数
		if(!(genType instanceof ParameterizedType)) return null;
		// 泛型信息
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		// 索引错误
		if(index >= params.length || index < 0) return null;
		// 不是class类型
		if(!(params[index] instanceof Class)) return null;

		Class<T> result = (Class<T>) params[index];

		return result;
	}
}